import time
import cPickle as pickle
import os
import math
import gobject

from util import device, config

import logging
logger = logging.getLogger('async.timesync')

last_reliable = None

class TimeSync:
    
    TIME = 460
    LAST_RELIABLE = 461
    
    def __init__(self, presence):
        
        if not config.get_bool('async', 'timesync-enabled'):
            # Disable this functionality
            return
        
        self.__presence = presence
        
        self.__time_file = os.path.join(os.path.dirname(__file__), 'time')
        logger.debug('Time file: %s. Exists? %s', self.__time_file, os.path.exists(self.__time_file))
        if os.path.exists(self.__time_file):
            f = open(self.__time_file, mode='rb')
            self.__last_reliable = pickle.load(f)
            f.close()
            logger.info('Loaded reliable time: %s', self.__last_reliable)
        else:
            self.__last_reliable = None
        
        self.__presence.connect('neighbor-online', self.__on_neighbor)
        self.__presence.connect('neighbor-update', self.__on_neighbor)
        
        # Minutes between updates
        # TODO: Replace with config
        minutes = 1
        gobject.timeout_add(1000*60*minutes, self.__update)
        self.__update()
        
    def __update(self):
        logger.info('Updating. Time: %s. Last reliable: %s', self.my_time(), self.__last_reliable)
        self.__presence.add_meta(TimeSync.TIME, self.my_time())
        self.__presence.add_meta(TimeSync.LAST_RELIABLE, self.__last_reliable)
        return True
    
    def __on_neighbor(self, presence, neighbor, new=False):
        other_time = neighbor.meta.get(TimeSync.TIME, None)
        other_last_reliable = neighbor.meta.get(TimeSync.LAST_RELIABLE, None)
        
        logger.info('Got neighbor input. Their time: %s. Their reliable time: %s.', other_time, other_last_reliable)
        if other_time and other_last_reliable:
            self.input_unreliable(other_time, other_last_reliable)
        
    def input_reliable(self, reliable_time):
        """
            Input the time from a reliable source.
            This source could be GPS, NTP, etc.
            
            @param reliable_time: A reliable time, in ticks
            @type reliable_time: float
        """
        logger.info('Inputting reliable time: %s', reliable_time)
        self.__last_reliable = reliable_time
        
        self.__set_time(reliable_time)
        
        f = open(self.__time_file, mode='wb')
        pickle.dump(self.__last_reliable, f)
        f.close()
    
    def input_unreliable(self, other_time, other_last_reliable):
        logger.info('Inputting neighbor time %s with last reliable %s',
                        other_time, other_last_reliable)
        logger.debug('Other_last_reliable>self.__last_reliable? %s', other_last_reliable > self.__last_reliable)
        if other_last_reliable > self.__last_reliable:
            # If more than 1/10th of a second apart, set my time to 
            # the time my neighbor is advertising.
            logger.debug('Other_time - self.my_time(): %s', other_time - self.my_time())
            if math.fabs(other_time - self.my_time()) > 0.1:
                logger.debug('My time: %s. Other time: %s. Setting to other.', self.my_time(), other_time)
                self.__set_time(other_time)
    
    def __set_time(self, new_time):
        logger.info('Setting time to %s', new_time)
        if device.current() in (device.NOKIA_TABLET, device.LINUX):
            time_string = time.strftime("%Y-%m-%d/%H:%M:%S",time.gmtime(new_time))
            exit_code = os.system('/mnt/initfs/usr/bin/retutime --set-time "%s"' % time_string)
            logger.info('Set time with time string %s. Exit code: %i', time_string, exit_code)
        else:
            raise NotImplementedError('No support for time setting on Windows yet')
        self.__update()
    
    def my_time(self):
        return time.time()
    
    def last_reliable(self):
        return self.__last_reliable